/**********************************************************/
/* Goblin Defense - A tower defense game for Wii Homebrew */
/* Copyright (C) 2009 Ricardo Bueno Cordeiro              */
/* Licensed under de GNU General Public License v2.0      */
/*   See license_gplv2.txt for detalis                    */
/**********************************************************/

#include <functional>

#include <gccore.h>
#include <wiiuse/wpad.h>

#include "map.h"
#include "draw.h"
#include "random.h"
#include "path.h"
#include "ponies/pony_normal.h"

#include "map_def.h"

Map::Map(Game *g) : game(g) {
	this->loaded_map = -1;
	this->selected_weapon = NULL;
}

void Map::load(int map) {
	if ((map < 0) || (map >= NUM_MAPS)) {
		this->loaded_map = -1;
		return;
	}

	this->unload();
	this->loaded_map = map;
	this->path = paths[map];

	this->remain = 0;
	this->selected_weapon = NULL;
}

void Map::unload() {
	this->selected_weapon = NULL;

	for (u32 i = 0; i < this->ponies.size(); i++)
		this->ponies.remove(i);
	for (u32 i = 0; i < this->weapons.size(); i++)
		this->weapons.remove(i);
	for (u32 i = 0; i < this->effects.size(); i++)
		this->effects.remove(i);

	// Commit changes
	this->ponies.commit();
	this->weapons.commit();
	this->effects.commit();
	
	this->loaded_map = -1;
	this->path = NULL;
}

int Map::add_pony(Pony *p) {
	if (loaded_map < 0)
		return 0;
	return this->ponies.add(p);
}

int Map::add_weapon(Weapon *w) {
	if (loaded_map < 0)
		return 0;
	return this->weapons.add(w);
}

int Map::add_weapon(UpgradableWeapon *w) {
	if (loaded_map < 0)
		return 0;

	if (!this->add_weapon(static_cast<Weapon *>(w)))
		return 0;
	return this->upgradable_weapons.add(w);
}

int Map::add_effect(Effect *e) {
	if (loaded_map < 0)
		return 0;
	return this->effects.add(e);
}

void Map::collision() {
	for (u32 i = 0; i < this->weapons.size(); i++) {
		u32 oldest_pony = this->ponies.size();

		for (u32 j = 0; j < this->ponies.size(); j++) {
			if (AreaItem::near(this->weapons[i], this->ponies[j]) && !this->ponies[j]->remove()) {
				if ((oldest_pony == this->ponies.size()) || (AreaItem::higher_priority(this->ponies[oldest_pony], this->ponies[j])))
					oldest_pony = j;
			}
		}

		if (oldest_pony < this->ponies.size())
			this->weapons[i]->pony_near_by(this->game, this->ponies[oldest_pony]);
	}
}

int Map::update(int state, u32 step, WPADData *wiimote_data) {
	if ((this->loaded_map < 0) || (state == CONTROL_HIDDEN))
		return 0;
	
	// Update sprites
	for (u32 i = 0; i < this->ponies.size(); i++)
		this->ponies[i]->update(step);
	for (u32 i = 0; i < this->weapons.size(); i++)
		this->weapons[i]->update(step);
	for (u32 i = 0; i < this->effects.size(); i++)
		this->effects[i]->update(step);

	// Resolve Collisions
	this->collision();

	// Erase references to expired sprites
	if ((this->selected_weapon != NULL) && (this->selected_weapon->remove()))
		this->selected_weapon = NULL;
	for (u32 i = 0; i < this->upgradable_weapons.size(); i++)
		if (this->upgradable_weapons[i]->remove())
			this->upgradable_weapons[i] = NULL;
		
	// Remove expired
	for (u32 i = 0; i < this->ponies.size(); i++)
		if (this->ponies[i]->remove())
			this->ponies.remove(i);
	for (u32 i = 0; i < this->weapons.size(); i++)
		if (this->weapons[i]->remove())
			this->weapons.remove(i);
	for (u32 i = 0; i < this->effects.size(); i++)
		if (this->effects[i]->remove())
			this->effects.remove(i);

	// Add new ponies
	this->remain += step;
	while (this->remain > 200) {
		this->add_pony(new PonyNormal(this->path));
		this->remain -= 200;
	}

	// Commit changes
	this->ponies.commit();
	this->weapons.commit();
	this->upgradable_weapons.commit();
	this->effects.commit();

	return 0;
}

void Map::draw(int state) {
	if ((this->loaded_map < 0) || (state == CONTROL_HIDDEN))
		return;
		
	DR_set_map_texture(this->loaded_map);

	// Draw Map
	GX_Begin(GX_QUADS, GX_VTXFMT0, 4);

	GX_Position3f32(-((f32)MAP_SIZE) / 2.0, -((f32)MAP_SIZE) / 2.0, 0.0);
	GX_Color4u8(255, 255, 255, 255);
	GX_TexCoord2f32(0.0, 1.0);

	GX_Position3f32(((f32)MAP_SIZE) / 2.0, -((f32)MAP_SIZE) / 2.0, 0.0);
	GX_Color4u8(255, 255, 255, 255);
	GX_TexCoord2f32(1.0, 1.0);

	GX_Position3f32(((f32)MAP_SIZE) / 2.0, ((f32)MAP_SIZE) / 2.0, 0.0);
	GX_Color4u8(255, 255, 255, 255);
	GX_TexCoord2f32(1.0, 0.0);

	GX_Position3f32(-((f32)MAP_SIZE) / 2.0, ((f32)MAP_SIZE) / 2.0, 0.0);
	GX_Color4u8(255, 255, 255, 255);
	GX_TexCoord2f32(0.0, 0.0);

	GX_End();

	for (u32 i = 0; i < this->ponies.size(); i++)
		this->ponies[i]->draw();
	for (u32 i = 0; i < this->weapons.size(); i++)
		this->weapons[i]->draw();
	for (u32 i = 0; i < this->effects.size(); i++)
		this->effects[i]->draw();

	DR_configure(DRAW_CONF_DRAW_NO_TEXTURE);
	if (this->selected_weapon != NULL)
		this->selected_weapon->draw_selected_area(255, 255, 255, 100);
}

void Map::text(int state) {
}

void Map::select_weapon(f32 x, f32 y) {
	f32 closest_dist = 512.0 * 512.0 * 2;
	f32 closest_weapon = this->weapons.size();

	f32 wx, wy, wr;
		
	for (u32 i = 0; i < this->upgradable_weapons.size(); i++) {
		wx = this->weapons[i]->get_x();
		wy = this->weapons[i]->get_y();
		wr = this->weapons[i]->get_radius();
		f32 dist = (wx - x) * (wx - x) + (wy - y) * (wy - y);
		
		if ((dist <= (wr * wr)) && (dist < closest_dist)) {
			closest_dist = dist;
			closest_weapon = i;
		}
	}

	this->selected_weapon = NULL;
	if (closest_weapon < this->upgradable_weapons.size())
		this->selected_weapon = this->upgradable_weapons[closest_weapon];
}

int Map::on_path(f32 x, f32 y, f32 r) {
	return 0;
}

